package com.vmj.instructions.references;

import com.vmj.heap.VMClass;
import com.vmj.heap.*;
import com.vmj.heap.VMObject;
import com.vmj.instructions.base.Index16Instruction;
import com.vmj.rtda.Frame;
import com.vmj.rtda.OperandStack;

/**
 * Created by yangshoujun on 2017/8/27 下午12:07.
 */
public class PutField extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        Method currentMethod = frame.getMethod();
        VMClass currentClazz = currentMethod.getClazz();
        RuntimeConstantPool cp = currentClazz.getRuntimeConstantPool();
        FieldRef fieldRef = (FieldRef) cp.getConstant(getIndex()).getValue();
        Field field = fieldRef.resolvedField();
        VMClass clazz = fieldRef.getClazz();

        if (field.isStatic()) {
            throw new RuntimeException("java.lang.IncompatibleClassChangeError");
        }

        // final类型变量只有当前类的构造方法可以设置值
        if (field.isFinal()) {
            if (currentClazz != clazz || !"<init>".equals(currentMethod.getName())) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
        }

        String descriptor = field.getDescriptor();
        int slotId = field.getSlotId();
        OperandStack stack = frame.getOperandStack();
        switch (descriptor.charAt(0)) {
            case 'Z':
            case 'B':
            case 'C':
            case 'S':
            case 'I':
                int val = stack.popInt();
                VMObject ref = stack.popRef();
                if (ref == null) {
                    throw new RuntimeException("java.lang.NullPointerException");
                }
                Slots si = (Slots) ref.getData();
                si.setInt(slotId, val);
                break;
            case 'F':
                float f = stack.popFloat();
                VMObject rf = stack.popRef();
                if (rf == null) {
                    throw new RuntimeException("java.lang.NullPointerException");
                }
                Slots sf = (Slots) rf.getData();
                sf.setFloat(slotId, f);
                break;
            case 'J':
                long l = stack.popLong();
                VMObject rl = stack.popRef();
                if (rl == null) {
                    throw new RuntimeException("java.lang.NullPointerException");
                }
                Slots sl = (Slots) rl.getData();
                sl.setLong(slotId, l);
                break;
            case 'D':
                double d = stack.popDouble();
                VMObject rd = stack.popRef();
                if (rd == null) {
                    throw new RuntimeException("java.lang.NullPointerException");
                }
                Slots sd = (Slots) rd.getData();
                sd.setDouble(slotId, d);
                break;
            case 'L':
            case '[':
                VMObject v = stack.popRef();
                VMObject r = stack.popRef();
                Slots sr = (Slots) r.getData();
                sr.setRef(slotId, v);
                break;
        }
    }
}
